/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：登录通-数据源-数据持久层
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taiping.dlt.strategy;

import com.taiping.dlt.application.constant.exception.DltErrorStatusEnum;
import com.taiping.dlt.application.constant.dao.DataSourceEnum;
import com.taiping.dlt.pojo.properties.DltProperty;
import com.taiping.dlt.repository.IRepository;
import com.taiping.dlt.strategy.core.RepositoryHandler;
import com.taipingframework.utility.exception.SystemInterrupterException;
import com.taipingframework.utility.exception.UtilityException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 仓储对象 - 策略工厂
 */
@Component
public class RepositoryHandlerFactory implements InitializingBean {

    private static final Map<String, Object> BEANS_MAP = new ConcurrentHashMap<>();

    private static ApplicationContext applicationContext;
    private static DltProperty dltProperty;

    public RepositoryHandlerFactory(ApplicationContext applicationContext) {
        RepositoryHandlerFactory.applicationContext = applicationContext;
        RepositoryHandlerFactory.dltProperty = applicationContext.getBean(DltProperty.class);
    }

    /**
     * 通过策略工厂创建具体的仓储对象(即，数据访问对象)
     */
    @SuppressWarnings("unchecked")
    public static <DAO> DAO createRepository(ParameterizedTypeReference<DAO> parameterizedTypeReference) {
        try {
            ResolvableType resolvableType = ResolvableType.forType(parameterizedTypeReference);
            String[] beanNamesForType = applicationContext.getBeanNamesForType(resolvableType);
            for (String beanName : beanNamesForType) {
                Object beanInstance = applicationContext.getBean(beanName);
                DataSourceEnum dataSource = beanInstance.getClass().getSuperclass().getAnnotation(RepositoryHandler.class).value();
                if (dltProperty.getDataSource() == dataSource) {
                    return (DAO) beanInstance;
                }
            }
            throw new SystemInterrupterException(DltErrorStatusEnum.UNABLE_GET_BEAN_REPOSITORY);
        } catch (Exception e) {
            throw new UtilityException(e);
        }
    }

    /**
     * 通过策略工厂创建具体的仓储对象(即，数据访问对象)
     */
    @SuppressWarnings("unchecked")
    public static <DO extends Object & Serializable> IRepository<DO> createRepository(Class<? extends IRepository> clazz, Class<? extends DO> generic) {
        ResolvableType resolvableType = ResolvableType.forClassWithGenerics(clazz, generic);
        return (IRepository<DO>) BEANS_MAP.get(resolvableType.toString());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        DataSourceEnum dataSource = dltProperty.getDataSource();
        Set<Map.Entry<String, Object>> entrySet = applicationContext.getBeansWithAnnotation(RepositoryHandler.class).entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            Object beanInstance = entry.getValue();
            Class<?> clazz = beanInstance.getClass().getSuperclass();
            DataSourceEnum actualDataSource = clazz.getAnnotation(RepositoryHandler.class).value();
            if (actualDataSource != dataSource) {
                continue;
            }
            if (!IRepository.class.isAssignableFrom(clazz)) {
                continue;
            }
            Class<?> genericClazz = ResolvableType.forType(clazz).getSuperType().getGeneric(0).resolve();
            ResolvableType resolvableType = ResolvableType.forClassWithGenerics(IRepository.class, genericClazz);
            BEANS_MAP.put(resolvableType.toString(), beanInstance);
        }
    }

}
